#pragma once

#include "particle.h"

#include <mutex>
#include <queue>
#include <semaphore>
#include <thread>
#include <memory>
#include <iostream>

namespace PS
{

using ptype = PS::Circle;
constexpr int N = 5;
extern std::counting_semaphore<N> number_of_queueing_particle;
extern std::counting_semaphore<N> number_of_empty_positions;
extern std::binary_semaphore state_of_simulation;
extern std::mutex buffer_manipulation;
extern std::mutex state_mutex;
extern std::queue<std::vector<ptype>> buffer;
extern bool _is_simulating;

inline bool is_simulating()
{
    std::lock_guard<std::mutex> g(state_mutex);
    return _is_simulating;
}

inline void start_simulate()
{
    std::lock_guard<std::mutex> g(state_mutex);
    _is_simulating = true;
}

inline void stop_simulate()
{
    std::lock_guard<std::mutex> g(state_mutex);
    _is_simulating = false;
}


inline void push_buffer(const std::vector<ptype>& p)
{
    number_of_empty_positions.acquire();
    {
        std::lock_guard<std::mutex> g(buffer_manipulation);
        buffer.push(std::vector<ptype>(p.begin(), p.end()));
        std::cout << "Push buffer\n";
    }
    number_of_queueing_particle.release();
}

inline void take_particle_from_buffer(std::vector<ptype>& particles)
{
    if (!number_of_queueing_particle.try_acquire()) return;

    {
        std::lock_guard<std::mutex> g(PS::buffer_manipulation);
        particles = buffer.front();
        buffer.pop();
    }
    number_of_empty_positions.release();

    std::cout << "take particle\n";
}

} // namespace PS
